#!/usr/bin/perl

######################################################################
package Swignition::uF::geo;
######################################################################

use Swignition::GenericParser::Utils;
use Swignition::uF;
use XML::LibXML qw(:all);
use strict;


sub consume
{
	my $page = shift;
	my $pageURL     = $page->uri;
	my $pageSubject = $page->{RDF}->{$pageURL};
	
	$page->{uF}->{geo} = ();
	
	# icbm
	my @icbm = $pageSubject->vals('icbm');
	if (@icbm)
	{
		foreach my $icbm (@icbm)
		{
			my ($lat, $long) = split /[\;\,]\s*/, $icbm;
			next unless ($lat || $long);
			my $geo = {
					'latitude'        => $lat,
					'longitude'       => $long,
					'reference-frame' => 'WGS 84'
				};
			push @{$page->{uF}->{geo}}, (bless $geo);
		}
	}

	unless ( $page->{Profiles}->{'http://www.w3.org/2006/03/hcard'}
	||       $page->{Profiles}->{'http://purl.org/uF/hCard/1.0/'}
	||       $page->{Profiles}->{'http://purl.org/uF/geo/0.9/'}
	||       $page->{Profiles}->{'http://purl.org/uF/2008/03/'}
	||      !$page->{options}->{uf_strict_profiles})
	{
		return;
	}
	
	# geo
	push @{$page->{uF}->{geo}}, parse_all($page);
	foreach my $geo (@{ $page->{uF}->{geo} })
		{ $page->mergeSubject( $geo->rdf_subject ); }
	if (defined $page->{uF}->{geo}->[0])
		{ $page->mergeNS( $page->{uF}->{geo}->[0] ); }
}


sub parse_all
{
	my $page   = shift;
	my $within = shift || $page->{DOM};
	my @nodes = searchClass('geo', $within);
	my @rv;
	
	foreach my $geo (@nodes)
	{
		my $geo_parsed = parse($page, $geo);
		push @rv, $geo_parsed;
	}
	
	return @rv;
} #/sub parse_all


sub uri
{
	my $this = shift;
	my $all  = shift;
	my @rv;
	
	unless ((defined $this->{body} && $this->{body} !~ /^\s*earth\s*$/i)
	||      (defined $this->{'reference-frame'} && $this->{'reference-frame'} !~ /wgs.*84/i ))
	{
		if (length $this->{altitude})
		{
			push @rv, sprintf('geo:%f,%f,%f', 
				$this->{latitude},
				$this->{longitude},
				$this->{altitude});
		}
		else
		{
			push @rv, sprintf('geo:%f,%f', 
				$this->{latitude},
				$this->{longitude});
		}
	}
	if (length $this->{_id})
	{
		push @rv, Swignition::uF::TDBURI($this->{_page}->uri.'#'.$this->{_id});
	}
	if (lc($this->{_dom}->tagName) eq 'body')
	{
		push @rv, Swignition::uF::TDBURI($this->{_page}->uri);
	}
	push @rv, Swignition::GenericParser::Utils::BNodeURI($this->{_dom}, 'GeoPoint')
		unless (@rv);
	
	return $rv[0] unless (defined $all);
	while ($all) { shift @rv; $all--; }
	return @rv;
} #/sub uri


sub rdf_subject
{
	my $this    = shift;
	my $subject = Swignition::RDFModel::Subject->new($this->uri);
	$subject->setDom($this->{_dom});
	$subject->addObject($this);
	
	if ( (defined $this->{body} && $this->{body} !~ /^\s*earth\s*$/i)
	||   (defined $this->{'reference-frame'} && $this->{'reference-frame'} !~ /wgs.*84/i ) )
	{
		$subject->addVal('http://www.w3.org/1999/02/22-rdf-syntax-ns#type',
			Swignition::RDFModel::Value->new("http://buzzword.org.uk/rdf/ungeo#Point", 1));

		my $comment   = Swignition::RDFModel::Value->new('Non-WGS84 geopositioning.');
		$subject->addVal('http://www.w3.org/2000/01/rdf-schema#comment', $comment);
		
		foreach my $v (qw(altitude longitude latitude))
		{
			if (length $this->{$v})
			{
				my $val = Swignition::RDFModel::Value->new($this->{$v});
				$subject->addVal('http://buzzword.org.uk/rdf/ungeo#'.$v, $val);
			}
		}

		my $rs = Swignition::RDFModel::Subject->new(
			Swignition::GenericParser::Utils::BNodeURI($this->{_dom}, 'ReferenceSystem'));
		$rs->addVal('http://www.w3.org/1999/02/22-rdf-syntax-ns#type',
			Swignition::RDFModel::Value->new("http://buzzword.org.uk/rdf/ungeo#ReferenceSystem", 1));
		$rs->addVal('http://buzzword.org.uk/rdf/ungeo#body',
			Swignition::RDFModel::Value->new($this->{'body'}))
			if (length $this->{'body'});
		$rs->addVal('http://purl.org/dc/terms/title',
			Swignition::RDFModel::Value->new($this->{'reference-frame'}))
			if (length $this->{'reference-frame'});
		$subject->addVal('http://buzzword.org.uk/rdf/ungeo#system',
			Swignition::RDFModel::Value->new($rs->uri, 1));
		$this->{_page}->mergeSubject($rs);
	}
	else
	{
		$subject->addVal('http://www.w3.org/1999/02/22-rdf-syntax-ns#type',
			Swignition::RDFModel::Value->new("http://www.w3.org/2003/01/geo/wgs84_pos#Point", 1));

		my $latitude  = Swignition::RDFModel::Value->new($this->{latitude});
		my $longitude = Swignition::RDFModel::Value->new($this->{longitude});
		
		$subject->addVal('http://www.w3.org/2003/01/geo/wgs84_pos#lat', $latitude);
		$subject->addVal('http://www.w3.org/2003/01/geo/wgs84_pos#long', $longitude);
		
		if (length $this->{altitude})
		{
			my $altitude = Swignition::RDFModel::Value->new($this->{altitude});
			$subject->addVal('http://www.w3.org/2003/01/geo/wgs84_pos#alt', $altitude);
		}
	}
	
	return $subject;
}


sub metadata_ns
{
	my $this = shift;
	return { 'geo'=> { 
		nsurl => 'http://www.w3.org/2003/01/geo/wgs84_pos#',
		title => 'Basic Geo (WGS84 lat/long) Vocabulary' 
	},
	'ungeo' => {
		nsurl => 'http://buzzword.org.uk/rdf/ungeo#',
		title => 'UNGEO'
	}};
}


sub parse
{
	my $page = shift;
	my $rv = { '_dom'=>shift };
	my $pkg = __PACKAGE__;
	if (defined $page->{uF}->{_Shortcuts}->{$pkg}->{ $rv->{_dom}->getAttribute('_xpath') })
		{ return $page->{uF}->{_Shortcuts}->{$pkg}->{ $rv->{_dom}->getAttribute('_xpath') }; }
	else
		{ $page->{uF}->{_Shortcuts}->{$pkg}->{ $rv->{_dom}->getAttribute('_xpath') } = $rv; }
	
	my $geo_node = $rv->{'_dom'}->cloneNode(1);

	my $id = $geo_node->getAttribute('id');
	$rv->{_id} = $id if (length $id);
	$rv->{_page} = $page;
	
	data_patterns($page, $geo_node);
	
	simple_parse(
		$rv,
		$geo_node,
		[
			['longitude',        'n1'],
			['latitude',         'n1'],
			['body',             '?'], # extension
			['reference-frame',  '?'], # extension
			['altitude',         'M?', {embedded=>'hMeasure'}] # extension
		]
	);
	
	# If missing longitude or latitude, brute force search!
	if (!length($rv->{longitude}) || !length($rv->{latitude}))
	{
		my $str = $geo_node->toString;
		if ($geo_node->localname eq 'img' || $geo_node->localname eq 'area')
			{ $str = $geo_node->getAttribute('alt'); }
			
		if ($str =~ / \s* \+?(\-?[0-9\.]+) \s* [\,\;] \s* \+?(\-?[0-9\.]+) \s* /x)
		{
			$rv->{latitude}  = $1;
			$rv->{longitude} = $2;
		}
	}
	
	bless $rv;
	return $rv;
	
} #/sub parse


sub validate
{
	my $this = shift;
	my @rv;

	push @rv, ['geo', $this->uri, 'E', 1, "No latitude.", "Co-ordinates need a latitude."]
		if (!length $this->{latitude});

	push @rv, ['geo', $this->uri, 'E', 2, "No longitude.", "Co-ordinates need a latitude."]
		if (!length $this->{longitude});

	if ( (defined $this->{body} && $this->{body} !~ /^\s*earth\s*$/i)
	||   (defined $this->{'reference-frame'} && $this->{'reference-frame'} !~ /wgs.*84/i ) )
	{
		push @rv, ['geo', $this->uri, 'W', 5, "Non-WGS84 Co-ordinates.", "Non-WGS84 co-ordinates are poorly supported by most parsers."];
	}
	
	else
	{
# TODO - this needs fixing!
#		push @rv, ['geo', $this->uri, 'E', 3, "Invalid latitude.", "Latitude must be between -90 and 90."]
#			if ($this->{latitude} > 90 || $this->{latitude} < -90);
#		push @rv, ['geo', $this->uri, 'E', 4, "Invalid longitude.", "Longitude must be between -180 and 180."]
#			if ($this->{longitude} > 180 || $this->{longitude} < -180);
	}
	
	return @rv;
}


1; #/package Swignition::uF::geo




